#include "sComponentInstance.h"
#include "sComponentObject.h"
#include "sActionHandler.h"
#include "sComponent.h"
#include <algorithm>
#include "sGUI.h"
   
namespace sgui
{
namespace inst
{

const res::sComponent& sComponentInstance::getComponent() 
{ 
    return m_component; 
}

const EControlState& sComponentInstance::getState() const 
{ 
    return m_state; 
}

void sComponentInstance::setState(const EControlState& state) 
{ 
    m_state = state; 

	for (std::deque<sComponentRectInstance*>::const_reverse_iterator it = m_componentRects.rbegin(); it != m_componentRects.rend(); it++)
	{
		(*it)->update();
	}
}

sComponentRectInstance* sComponentInstance::getRequiredComponentRect(u32 idx)
{
    return m_requiredRects[idx];
}

const sComponentRectInstance* sComponentInstance::getRequiredComponentRect(u32 idx) const
{
    return m_requiredRects[idx];
}
    
bool sComponentInstance::OnHandlerEvent(const sActionHandlerEvent& event)
{
    return false;
}

bool sComponentInstance::OnEvent(const obs::sEvent& event)
{
	// check for child objects
	for (CONTAINER_WRAPPER(sComponentInstance)::reverse_iterator it = m_child.rbegin(); it != m_child.rend(); it++)
	{
		if (it->second->OnEvent(event))
		{
			return true;
		}
	}

	// check for component rects
	for (std::deque<sComponentRectInstance*>::const_reverse_iterator it = m_componentRects.rbegin(); it != m_componentRects.rend(); it++)
	{
		if ((*it)->OnEvent(event))
		{
			return true;
		}
	}

	return false;
}
    
bool sComponentInstance::OnPress(const obs::sMousePressEvent& event, sComponentRectInstance* rect)
{
    return true;
}
	
void sComponentInstance::setSize(math::sPointf newSize)
{
	math::sPointf sizeDiff = newSize - m_rect.getSize();
	m_rect.setSize(newSize);
	for (std::deque<sComponentRectInstance*>::iterator it = m_componentRects.begin(); it != m_componentRects.end(); it++)
	{
		(*it)->getComponent().getAlign().applyResize((*it)->getRect(), sizeDiff);
		(*it)->update();
	}
}

void sComponentInstance::setPosition(math::sPointf newPosition)
{
	m_rect.setPosition(newPosition);

	for (CONTAINER_WRAPPER(sComponentInstance)::iterator it = m_child.begin(); it != m_child.end(); it++)
	{
		it->second->updateOffset();
	}
	for (std::deque<sComponentRectInstance*>::iterator it = m_componentRects.begin(); it != m_componentRects.end(); it++)
	{
		(*it)->updateOffset();
	}
}
	
void sComponentInstance::updateOffset()
{
	if (m_parent)
	{
		m_offset = m_parent->getOffset() + m_parent->getRect().getPosition();
	}
	else
	{
		m_offset = math::sPointf(0, 0);
	}

	for (CONTAINER_WRAPPER(sComponentInstance)::iterator it = m_child.begin(); it != m_child.end(); it++)
	{
		it->second->updateOffset();
	}
	for (std::deque<sComponentRectInstance*>::iterator it = m_componentRects.begin(); it != m_componentRects.end(); it++)
	{
		(*it)->updateOffset();
	}
}
	
void sComponentInstance::update(double dt)
{
	for (CONTAINER_WRAPPER(sComponentInstance)::iterator it = m_child.begin(); it != m_child.end(); it++)
	{
		it->second->update(dt);
	}
}
	
sComponentRectInstance* sComponentInstance::getRectAt(const math::sPointf point)
{
	if (getRect().pointIn(point))
	{
        const math::sPointf point_child = point - getRect().getPosition();
		for (CONTAINER_WRAPPER(sComponentInstance)::reverse_iterator it = m_child.rbegin(); it != m_child.rend(); it++)
		{
            sComponentRectInstance* rect = (it)->second->getRectAt(point_child);
			if (rect)
				return rect;
		}
		for (std::deque<sComponentRectInstance*>::reverse_iterator it = m_componentRects.rbegin(); it != m_componentRects.rend(); it++)
		{
            if ((*it)->getRect().pointIn(point_child))
            {
                return *it;
            }
		}
	}

	return NULL;
}
	
void sComponentInstance::draw(engine::sDrawStack& drawStack) const
{
	ALLOC_CONTEXT(getGui().getEngine(), contextRect, m_rect);
	if (drawStack.push(contextRect))
	{
		for (std::deque<sComponentRectInstance*>::const_iterator it = m_componentRects.begin(); it != m_componentRects.end(); it++)
		{
			(*it)->draw(drawStack);
		}

		for (CONTAINER_WRAPPER(sComponentInstance)::const_iterator it = m_child.begin(); it != m_child.end(); it++)
		{
			it->second->draw(drawStack);
		}

		drawStack.pop();
	}
	RELEASE_CONTEXT(contextRect);
}
	
sComponentInstance::sComponentInstance(sGUI& gui, const res::sComponentObject& object, sComponentInstance* parent) : 
	m_state(CS_NORMAL), 
	m_component(*object.getComponent().get()), 
    m_rect(object.getComponent().get()->getRect()),
	m_componentObject(object),
	sNamedObject(gui, object.getName()),
	m_child(gui),
	m_parent(parent)
{
    const std::deque<res::sComponentRect*>& rects = m_component.getComponentRects();
    const std::deque<res::sRequiredComponentRect>& rects_req = m_component.getRequiredComponentRects();

    for (std::deque<res::sComponentRect*>::const_iterator it = rects.begin(); it != rects.end(); it++)
    {
        m_componentRects.push_back((*it)->getInstance(*this, -1));
    }

    if (m_component.isValid())
    {
        for (std::deque<res::sRequiredComponentRect>::const_iterator it = rects_req.begin(); it != rects_req.end(); it++)
        {
            std::deque<res::sComponentRect*>::const_iterator pos = std::find(rects.begin(), rects.end(), (*it).m_rect);
            if (pos != rects.end())
            {
                u32 index = pos - rects.begin();
                m_componentRects[index]->setID(m_requiredRects.size());
                m_requiredRects.push_back(m_componentRects[index]);
            }            
        }
    }

	const CONTAINER_WRAPPER(res::sComponentObject)& child = object.getChild();

	for (CONTAINER_WRAPPER(res::sComponentObject)::const_iterator it = child.begin(); it != child.end(); it++)
	{
		sComponentInstance* child = (*it).second->getInstance(gui, this);
		if (child)
		{
			m_child.add(child);
		}
	}

    setSize(object.getRect().getSize());

	updateOffset();
}

sComponentInstance::~sComponentInstance()
{
    for (std::deque<sComponentRectInstance*>::iterator it = m_componentRects.begin(); it != m_componentRects.end(); it++)
    {
        delete *it;
    }
    m_componentRects.clear();
	m_child.clear();
}

};
};